package com.ruoyi.web.controller.iot;

import com.github.pagehelper.PageHelper;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.domain.enums.DeviceTimeScope;
import com.ruoyi.iot.service.DeviceAlarmRecordService;
import com.ruoyi.iot.service.HostDeviceMetricsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 主机设备功率与运行统计接口.
 */
@Tag(name = "主机设备功率与运行统计接口")
@RestController
@RequestMapping("/iot/host-device")
public class HostDeviceMetricsController extends BaseController {

    private static final String HOST_DEVICE_CACHE_PREFIX = "iot:host-device";
    private static final int METRICS_CACHE_TTL_SECONDS = 3600;
    private static final int DAY_CACHE_TTL_SECONDS = 86400;
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final String TAG_HOST_DEVICE_METRICS = "主机设备功率与运行统计接口";

    private final HostDeviceMetricsService metricsService;
    private final DeviceAlarmRecordService alarmRecordService;
    private final RedisCache redisCache;

    public HostDeviceMetricsController(HostDeviceMetricsService metricsService,
                                       DeviceAlarmRecordService alarmRecordService,
                                       RedisCache redisCache) {
        this.metricsService = metricsService;
        this.alarmRecordService = alarmRecordService;
        this.redisCache = redisCache;
    }

    @Operation(summary = "获取所有主机柜设备及其开关柜", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/all-main-cabinet")
    public AjaxResult queryAllMainCabinetMetrics() {
        List<Map<String, Object>> list = metricsService.queryAllMainCabinetMetrics();
        return AjaxResult.success(list);
    }

    @Operation(summary = "查询主机设备在指定范围内的有功功率累积", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/active-power/total")
    public AjaxResult queryTotalActivePower(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceId,
        @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("active-power:total",
            "deviceKgId:" + deviceId,
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> {
            BigDecimal total = metricsService.queryTotalActivePower(deviceId, timeScope);
            Map<String, Object> data = new HashMap<>(4);
            data.put("deviceKgId", deviceId);
            data.put("scope", timeScope.getCode());
            data.put("scopeName", timeScope.getDescription());
            data.put("totalActivePowerKw", total.setScale(2, RoundingMode.HALF_UP));
            return data;
        });
    }


    /**
     * 查询设备历史健康评分趋势。
     */
    @Operation(summary = "查询设备历史健康评分趋势", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/health-score/history")
    public AjaxResult queryHealthScoreHistory(
            @Parameter(name = "deviceId", description = "设备编号", required = true)
            @RequestParam("deviceId") String deviceId,
            @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
            @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        List<Map<String, Object>> history = metricsService.queryHealthScoreHistory(deviceId, timeScope);
        Map<String, Object> data = new HashMap<>(4);
        data.put("deviceId", deviceId);
        data.put("scope", timeScope.getCode());
        data.put("scopeName", timeScope.getDescription());
        data.put("history", history);
        return AjaxResult.success(data);
    }

    @Operation(summary = "查询主机设备最新健康评分", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/health-score/latest")
    public AjaxResult queryLatestHealthScore(
            @Parameter(name = "deviceId", description = "设备编号", required = true)
            @RequestParam("deviceId") String deviceId) {
        Map<String, Object> latest = metricsService.queryLatestHealthScore(deviceId);
        Map<String, Object> data = new HashMap<>(2);
        data.put("deviceId", deviceId);
        data.put("latest", latest);
        return AjaxResult.success(data);
    }

    @Operation(summary = "统计主机设备各数据来源的告警数量", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/alarm-records/summary")
    public AjaxResult summarizeAlarmLevels(
            @Parameter(name = "deviceKgId", description = "设备编号", required = true)
            @RequestParam("deviceKgId") String deviceKgId,
            @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
            @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        Map<String, Long> counts = alarmRecordService.countAlarmLevels(deviceKgId, timeScope);
        Map<String, Object> data = new HashMap<>(counts);
        data.put("deviceId", deviceKgId);
        data.put("scope", timeScope.getCode());
        data.put("scopeName", timeScope.getDescription());
        return AjaxResult.success(data);
    }




    @Operation(summary = "查询主机设备在指定范围内的无功功率累积", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/reactive-power/total")
    public AjaxResult queryTotalReactivePower(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceId,
        @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("reactive-power:total",
            "deviceKgId:" + deviceId,
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> {
            BigDecimal total = metricsService.queryTotalReactivePower(deviceId, timeScope);
            Map<String, Object> data = new HashMap<>(4);
            data.put("deviceKgId", deviceId);
            data.put("scope", timeScope.getCode());
            data.put("scopeName", timeScope.getDescription());
            data.put("totalReactivePowerKw", total.setScale(2, RoundingMode.HALF_UP));
            return data;
        });
    }

    @Operation(summary = "查询主机设备今日/本月/本年度用电量（kWh）", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/active-energy/summary")
    public AjaxResult queryActiveEnergySummary(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceKgId,
        @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("active-energy:summary",
            "deviceKgId:" + deviceKgId,
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> {
            List<Map<String, Object>> summary = metricsService.queryActiveEnergySummary(deviceKgId, timeScope);
            Map<String, Object> data = new HashMap<>(2);
            data.put("deviceKgId", deviceKgId);
            data.put("summary", summary);
            return data;
        });
    }

    @Operation(summary = "汇总全部主机在指定范围内的运行时长、功率及用电量", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/metrics/aggregate")
    public AjaxResult aggregateHostMetrics(
        @Parameter(name = "scope", description = "统计范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("metrics:aggregate",
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> metricsService.aggregateHostMetrics(timeScope));
    }

    @Operation(summary = "查询全部主机在指定范围内是否运行过", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/run-status")
    public AjaxResult queryHostRunStatus(
        @Parameter(name = "scope", description = "统计范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("run-status", "scope:" + timeScope.getCode());
        List<Map<String, Object>> cached = redisCache.getCacheObject(cacheKey);
        if (cached != null) {
            return AjaxResult.success(cached);
        }
        List<Map<String, Object>> statusList = metricsService.queryHostRunStatus(timeScope);
        redisCache.setCacheObject(cacheKey, statusList, DAY_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
        return AjaxResult.success(statusList);
    }

    @Operation(summary = "分页查询主机运行报表列表", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/operation-report")
    public TableDataInfo queryHostOperationReport(
        @Parameter(name = "scope", description = "统计范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope,
        @Parameter(name = "deviceKgId", description = "可选，主机开关柜编号")
        @RequestParam(value = "deviceKgId", required = false) String deviceKgId) {
        DeviceTimeScope timeScope = resolveScope(scope);
        PageDomain pageDomain = TableSupport.buildPageRequest();
        int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum();
        int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize();
        String cacheKey = buildCacheKey("operation-report",
            "scope:" + timeScope.getCode(),
            StringUtils.isBlank(deviceKgId) ? "device:ALL" : "device:" + deviceKgId);
        List<Map<String, Object>> cached = redisCache.getCacheObject(cacheKey);
        if (cached == null) {
            cached = metricsService.queryHostOperationReport(timeScope, deviceKgId);
            redisCache.setCacheObject(cacheKey, cached, DAY_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
        }
        return buildManualTable(cached, pageNum, pageSize);
    }



    @Operation(summary = "统计主机设备在指定范围内的总用电量（kWh）", tags = TAG_HOST_DEVICE_METRICS)
    // @GetMapping("/active-energy/total")
    public AjaxResult queryTotalActiveEnergy(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceId,
        @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        BigDecimal totalEnergy = metricsService.queryTotalActiveEnergy(deviceId, timeScope);
        Map<String, Object> data = new HashMap<>(4);
        data.put("deviceKgId", deviceId);
        data.put("scope", timeScope.getCode());
        data.put("scopeName", timeScope.getDescription());
        data.put("totalEnergyKwh", totalEnergy);
        return AjaxResult.success(data);
    }

    @Operation(summary = "查询主机设备最新一条实时数据", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/metrics/latest")
    public AjaxResult queryLatestMetrics(
        @Parameter(name = "deviceId", description = "设备编号", required = true)
        @RequestParam("deviceId") String deviceId) {
        List<Map<String, Object>> latest = metricsService.queryLatestRecord(deviceId);
        Map<String, Object> data = new HashMap<>(2);
        data.put("deviceId", deviceId);
        data.put("latest", latest);
        return AjaxResult.success(data);
    }

    @Operation(summary = "查询主机设备在指定范围内的运行时长累计（h）", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/runtime/total")
    public AjaxResult queryTotalRuntime(
        @Parameter(name = "deviceKgId", description = "主机开关柜编号", required = true)
        @RequestParam("deviceKgId") String deviceKgId,
        @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("runtime:total",
            "deviceKgId:" + deviceKgId,
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> {
            BigDecimal total = metricsService.queryTotalRuntimeHours(deviceKgId, timeScope);
            Map<String, Object> data = new HashMap<>(4);
            data.put("deviceKgId", deviceKgId);
            data.put("scope", timeScope.getCode());
            data.put("scopeName", timeScope.getDescription());
            data.put("runtimeHours", total.setScale(2, RoundingMode.HALF_UP));
            return data;
        });
    }

    @Operation(summary = "统计主机设备在指定范围内的运行时长趋势（h）", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/runtime/trend")
    public AjaxResult queryRuntimeTrend(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceKgId,
        @Parameter(name = "scope", description = "统计范围（本日、本月、本季度、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        String cacheKey = buildCacheKey("runtime:trend",
            "deviceKgId:" + deviceKgId,
            "scope:" + timeScope.getCode());
        return respondWithCache(cacheKey, () -> {
            List<Map<String, Object>> trend = metricsService.queryRuntimeTrend(deviceKgId, timeScope);
            Map<String, Object> data = new HashMap<>(4);
            data.put("deviceId", deviceKgId);
            data.put("scope", timeScope.getCode());
            data.put("scopeName", timeScope.getDescription());
            data.put("trend", trend);
            return data;
        });
    }

    /**
     * 分页查询主机温度与开关柜电参数据，并合并成单条记录返回。
     */
    @Operation(summary = "分页查询开关柜电参与主机温度", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/device-snapshots/page")
    public AjaxResult queryCombinedDeviceSnapshots(
        @Parameter(name = "deviceKgId", description = "开关柜设备编号", required = true)
        @RequestParam("deviceKgId") String deviceKgId,
        @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope,
        @Parameter(name = "startTime", description = "自定义开始时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "startTime", required = false) String startTime,
        @Parameter(name = "endTime", description = "自定义结束时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "endTime", required = false) String endTime,
        @Parameter(name = "pageNum", description = "页码，默认1")
        @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
        @Parameter(name = "pageSize", description = "每页数量，默认10")
        @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) {
        DeviceTimeScope timeScope = resolveScope(scope);
        LocalDateTime customStart = parseDateTime(startTime);
        LocalDateTime customEnd = parseDateTime(endTime);
        Map<String, Object> data = metricsService.queryCombinedDeviceSnapshots(
            deviceKgId, timeScope, pageNum, pageSize, customStart, customEnd);
        return AjaxResult.success(data);
    }

    /**
     * 查询主机温度字段的极值统计。
     */
    @Operation(summary = "统计主机温度极值", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/device-temperature/stats")
    public AjaxResult queryDeviceTemperatureStats(
        @Parameter(name = "deviceId", description = "主机设备编号", required = true)
        @RequestParam("deviceId") String deviceId,
        @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope,
        @Parameter(name = "startTime", description = "自定义开始时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "startTime", required = false) String startTime,
        @Parameter(name = "endTime", description = "自定义结束时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "endTime", required = false) String endTime) {
        DeviceTimeScope timeScope = resolveScope(scope);
        LocalDateTime customStart = parseDateTime(startTime);
        LocalDateTime customEnd = parseDateTime(endTime);
        List<Map<String, Object>> stats = metricsService.queryDeviceTemperatureStats(
            deviceId, timeScope, customStart, customEnd);
        Map<String, Object> data = new HashMap<>(4);
        data.put("deviceId", deviceId);
        data.put("scope", timeScope.getCode());
        data.put("scopeName", timeScope.getDescription());
        data.put("items", stats);
        return AjaxResult.success(data);
    }

    /**
     * 查询主机温度趋势数据。
     */
    @Operation(summary = "查询主机温度趋势", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/device-temperature/trend")
    public AjaxResult queryDeviceTemperatureTrend(
        @Parameter(name = "deviceId", description = "主机设备编号", required = true)
        @RequestParam("deviceId") String deviceId,
        @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope,
        @Parameter(name = "startTime", description = "自定义开始时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "startTime", required = false) String startTime,
        @Parameter(name = "endTime", description = "自定义结束时间，格式yyyy-MM-dd HH:mm:ss")
        @RequestParam(value = "endTime", required = false) String endTime) {
        DeviceTimeScope timeScope = resolveScope(scope);
        LocalDateTime customStart = parseDateTime(startTime);
        LocalDateTime customEnd = parseDateTime(endTime);
        List<Map<String, Object>> trend = metricsService.queryDeviceTemperatureTrend(
            deviceId, timeScope, customStart, customEnd);
        Map<String, Object> data = new HashMap<>(4);
        data.put("deviceId", deviceId);
        data.put("scope", timeScope.getCode());
        data.put("scopeName", timeScope.getDescription());
        data.put("items", trend);
        return AjaxResult.success(data);
    }

    @Operation(summary = "分页查询主机设备故障记录", tags = TAG_HOST_DEVICE_METRICS)
    @GetMapping("/fault-records")
    public TableDataInfo queryFaultRecords(
        @Parameter(name = "deviceKgId", description = "设备编号", required = true)
        @RequestParam("deviceKgId") String deviceKgId,
        @Parameter(name = "scope", description = "时间范围（本日、本月、本年度），默认本月")
        @RequestParam(value = "scope", required = false) String scope) {
        DeviceTimeScope timeScope = resolveScope(scope);
        startPageWithDefaultSize(5);
        List<Map<String, Object>> records = metricsService.queryFaultRecords(deviceKgId, timeScope);
        return getDataTable(records);
    }

    private AjaxResult respondWithCache(String cacheKey, Supplier<Object> supplier) {
        Object cached = redisCache.getCacheObject(cacheKey);
        if (cached != null) {
            return AjaxResult.success(cached);
        }
        Object data = supplier.get();
        redisCache.setCacheObject(cacheKey, data, METRICS_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
        return AjaxResult.success(data);
    }

    private String buildCacheKey(String metric, String... dimensions) {
        StringBuilder builder = new StringBuilder(HOST_DEVICE_CACHE_PREFIX);
        builder.append(':').append(metric);
        builder.append(":area=").append(StringUtils.defaultString(RuoYiConfig.getArea(), "default"));
        if (dimensions != null) {
            for (String dimension : dimensions) {
                if (StringUtils.isBlank(dimension)) {
                    continue;
                }
                builder.append(':').append(dimension);
            }
        }
        return builder.toString();
    }

    private void startPageWithDefaultSize(int defaultSize) {
        int pageNum = Convert.toInt(ServletUtils.getParameter(TableSupport.PAGE_NUM), 1);
        String pageSizeText = ServletUtils.getParameter(TableSupport.PAGE_SIZE);
        int pageSize = StringUtils.isBlank(pageSizeText)
            ? defaultSize
            : Convert.toInt(pageSizeText, defaultSize);
        PageHelper.startPage(pageNum, pageSize);
    }

    private TableDataInfo buildManualTable(List<?> rows, int pageNum, int pageSize) {
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(200);
        rspData.setMsg("查询成功");
        if (rows == null || rows.isEmpty()) {
            rspData.setRows(Collections.emptyList());
            rspData.setTotal(0L);
            return rspData;
        }
        int total = rows.size();
        int fromIndex = Math.max((pageNum - 1) * pageSize, 0);
        if (fromIndex >= total) {
            fromIndex = 0;
            pageNum = 1;
        }
        int toIndex = Math.min(fromIndex + pageSize, total);
        rspData.setRows(rows.subList(fromIndex, toIndex));
        rspData.setTotal(total);
        return rspData;
    }

    private DeviceTimeScope resolveScope(String scope) {
        try {
            return DeviceTimeScope.fromText(scope);
        } catch (IllegalArgumentException ex) {
            throw new ServiceException("时间范围仅支持本日、本月、本季度、本年度");
        }
    }

    private LocalDateTime parseDateTime(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
            return LocalDateTime.parse(text.trim(), DATE_TIME_FORMATTER);
        } catch (DateTimeParseException ex) {
            throw new ServiceException("时间格式需为 yyyy-MM-dd HH:mm:ss");
        }
    }
}
